from __future__ import annotations

import smtplib
from email.message import EmailMessage as StdEmailMessage
from typing import Optional

from .config import SMTPConfig
from .provider import EmailMessage
from .exceptions import SMTPConnectionError, SMTPAuthenticationError, SMTPSendError


class SMTPEmailProvider:
    def __init__(self, cfg: SMTPConfig) -> None:
        self._cfg = cfg

    def _build(self, msg: EmailMessage) -> StdEmailMessage:
        m = StdEmailMessage()
        m["Subject"] = msg.subject
        m["From"] = f"{self._cfg.from_name} <{self._cfg.from_email}>"
        if msg.to:
            m["To"] = ", ".join(msg.to)
        if msg.cc:
            m["Cc"] = ", ".join(msg.cc)
        if msg.body_html and msg.body_text:
            m.set_content(msg.body_text)
            m.add_alternative(msg.body_html, subtype="html")
        elif msg.body_html:
            m.add_alternative(msg.body_html, subtype="html")
        else:
            m.set_content(msg.body_text or "")

        if msg.attachments:
            for att in msg.attachments:
                filename = att.get("filename")
                content = att.get("content_bytes", b"")
                mime_type = att.get("mime_type", "application/octet-stream")
                maintype, _, subtype = mime_type.partition("/")
                m.add_attachment(content, maintype=maintype, subtype=subtype, filename=filename)
        return m

    def send(self, msg: EmailMessage) -> str:
        m = self._build(msg)
        to_addrs = []
        if msg.to:
            to_addrs.extend(msg.to)
        if msg.cc:
            to_addrs.extend(msg.cc)
        if msg.bcc:
            to_addrs.extend(msg.bcc)

        try:
            # Use SSL for port 465, STARTTLS for other ports like 587
            if self._cfg.port == 465:
                # SSL connection
                with smtplib.SMTP_SSL(self._cfg.host, self._cfg.port, timeout=30) as s:
                    if self._cfg.username and self._cfg.password:
                        s.login(self._cfg.username, self._cfg.password)
                    s.send_message(m, from_addr=self._cfg.from_email, to_addrs=to_addrs)
                    return m.get("Message-Id", "") or ""
            elif self._cfg.use_tls:
                # STARTTLS connection
                with smtplib.SMTP(self._cfg.host, self._cfg.port, timeout=30) as s:
                    s.starttls()
                    if self._cfg.username and self._cfg.password:
                        s.login(self._cfg.username, self._cfg.password)
                    s.send_message(m, from_addr=self._cfg.from_email, to_addrs=to_addrs)
                    return m.get("Message-Id", "") or ""
            else:
                # Plain connection (not recommended)
                with smtplib.SMTP(self._cfg.host, self._cfg.port, timeout=30) as s:
                    if self._cfg.username and self._cfg.password:
                        s.login(self._cfg.username, self._cfg.password)
                    s.send_message(m, from_addr=self._cfg.from_email, to_addrs=to_addrs)
                    return m.get("Message-Id", "") or ""
        except smtplib.SMTPConnectError as e:
            raise SMTPConnectionError(f"Failed to connect to SMTP server {self._cfg.host}:{self._cfg.port}") from e
        except smtplib.SMTPAuthenticationError as e:
            raise SMTPAuthenticationError(f"SMTP authentication failed for user {self._cfg.username}") from e
        except (smtplib.SMTPException, OSError) as e:
            raise SMTPSendError(f"Failed to send email: {str(e)}") from e


